﻿
package extremefx.modules.seaDragon {
	import extremefx.conditions.ensuresBool;	
	import extremefx.IComparable;
	import extremefx.IDisposable;
	import extremefx.anim.Tween;
	import extremefx.geom.XPoint;
	
	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;		

	/**
	 * @author Marcelo Volmaro
	 */
	public final class Tile implements IComparable, IDisposable {
		private static var _downloading:uint = 0;
		private static var _jobs:Array = [];
		private static var _tilesLoaded:Array = [];
		
		private var _overlap:XPoint;
		private var _overn:uint;
		
		private var _lastTouchTime:uint;
		private var _url:String;
		private var _beingDrawn:Boolean;

		public var tileSize:uint;
		public var _loaded:Boolean;
		public var _loading:Boolean;
		public var bounds:Rectangle;
		
		public var _exists:Boolean;
		private var _level:uint;
		
		private var _y:uint;
		private var _x:uint;
		
		private var _img:Bitmap;
		
		public var _visibility:Number;
		public var _distance:Number;

		private var _tween:Tween;
		private var _holder:Sprite;
//		private var _alphaComplete:Boolean;
//		private var _alphaStart:Boolean;

		public function Tile(pLevel:uint, pX:uint, pY:uint, pBounds:Rectangle, pExists:Boolean, pUrl:String) {
			_level = pLevel;
			_x = pX;
			_y = pY;
			bounds = pBounds;
			_exists = pExists;
			_url = pUrl;

			_img = null; 
			_lastTouchTime = _visibility = _distance = 0;
			_loading = _loaded = _beingDrawn = false;
		}
		
		public function get overlap():uint {
			return _overn;
		}
		
		public function set overlap(pOverlap:uint):void {
			if (pOverlap){
				_overlap = null;
				
			} else {
				_overlap = new XPoint(1, 1);	
			}
			
			_overn = pOverlap;
		}

		public function touch():void {
			_lastTouchTime = getTimer();
			_beingDrawn = false;
		}

		public function load(pHolder:Sprite):void {
			
			if (_downloading >= Config.imageLoaderLimit) {
				_loading = false;
				return;
			}
			
			_holder = Sprite(pHolder.getChildAt(_level)); 
			_jobs[_downloading]= new ImageLoader(_url, _onComplete);
			++_downloading;
			_loading = true;
		}
		
		private function _onComplete(pItem:Bitmap):void {
			_downloading--;
			
			_loading = false;
			
			if (pItem == null){
				_exists = false;
				trace("Tile " + toString() + " failed to load: " + _url);
				return;
			}
			
			_loaded = true;
			
			pItem.alpha = 0;
			pItem.smoothing = true;
//			pItem.pixelSnapping = PixelSnapping.NEVER;
			_img = pItem;
			_tween = new Tween(_img, Config.blendTime);
			_tween.onComplete.add(alphaDone);
			
			var insertionIndex:uint = _tilesLoaded.length;
			if (_tilesLoaded.length >= 100) {
				var cutoff:Number = Math.ceil(Math.log(tileSize) / Math.log(2));
				var worstTile:Tile = null;
				var worstTileIndex:Number = -1;
				
				for (var i:int = _tilesLoaded.length - 1; i >= 0; i--) {
					var prevTile:Tile = _tilesLoaded[i];
					
					if (prevTile._level <= cutoff || prevTile._beingDrawn) {
						continue;
						
					} else if (!worstTile) {
						worstTile = prevTile;
						worstTileIndex = i;
						continue;
					}
					
					var prevTime:uint = prevTile._lastTouchTime;
					var worstTime:uint = worstTile._lastTouchTime;
					var prevLevel:uint = prevTile._level;
					var worstLevel:uint = worstTile._level;
					
					if (prevTime < worstTime || (prevTime == worstTime && prevLevel > worstLevel)) {
						worstTile = prevTile;
						worstTileIndex = i;
					}
				}
				
				if (worstTile && worstTileIndex >= 0) {
					worstTile.dispose();
					insertionIndex = worstTileIndex;
				}
			}
			
			_tilesLoaded[insertionIndex] = this;
			_lastTouchTime = getTimer();
		}
		
		public function remove():void {
			if (_img.parent is Sprite) {
				_img.parent.removeChild(_img);	
			}
		}
		
		public var alphaDone:Function = function():void{};
		
		public function setBounds(positionC:XPoint, sizeC:XPoint):void {
			if (!_img) return;
			
			if (_overlap){
				sizeC = sizeC.plus(_overlap);
			}
			
			_img.x = positionC.x;
			_img.y = positionC.y;
			_img.width = sizeC.x;
			_img.height = sizeC.y;
		}

		public function draw():void {
			ensuresBool(_loaded).isTrue("Attempting to draw tile " + toString() + " when it's not yet loaded.");
			
			_beingDrawn = true;
			
			if (!(_img.parent == _holder)) {
				_holder.addChild(_img);
				
				if (_img.alpha != 1){
					_tween.setProperty("alpha", 1);
				}
			}
		}

		public function toString():String {
			return _level + "/" + _x + "_" + _y;
		}
		
		public function dispose():void {
			if (_img && (_img.parent is Sprite)) _img.parent.removeChild(_img);
			_img.bitmapData.dispose();
		}
		
		public function compareTo(pObj:*):int {
			if (!(pObj is Tile)) {
				return 1;
			}
			
			var t:Tile = Tile(pObj);
			
			if (_visibility > t._visibility) {
				return 1;
				
			} else if (_visibility < t._visibility) {
				return -1;
			}
			
			if (_distance < t._distance) {
				return 1;
			}
			
			return -1;
		}
	}
}

import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLRequest;
import flash.utils.clearTimeout;
import flash.utils.setTimeout;

final class ImageLoader {
	private var _mcloader:Loader;
	private var _timeout:uint;
	private var _item:Bitmap;
	private var _complete:Function;

	public function ImageLoader(pSource:String, pOnComplete:Function) {
		_complete = pOnComplete;
		_item = null;
		
		var mcLoader:Loader = _mcloader = new Loader();
		mcLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onComplete, false, 0, true);
		mcLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR , _onError, false, 0, true);
		mcLoader.load(new URLRequest(pSource));
		
		_timeout = setTimeout(_onError, 5000);
	}

	private function _onError(event:IOErrorEvent = null):void {
		_finish(false);
	}

	private function _onComplete(event:Event):void {
		_item = Bitmap(_mcloader.content);
		_finish(true);
	}
	
	private function _finish(pSuccess:Boolean):void {
		if (_timeout) clearTimeout(_timeout);
		_mcloader.removeEventListener(Event.COMPLETE, _onComplete);
		_mcloader.removeEventListener(IOErrorEvent.IO_ERROR , _onError);
		_mcloader = null;
		_complete(_item);
	}
}
